﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Vanara.Interop
{
	internal static partial class NativeMethods
	{
		/*
		public static class BufferedPaint
		{
			public delegate void PaintAction<in TState, in TParam>(Graphics g, Rectangle rc, TState state, TParam optParam);

			private class BufferedPaintHandle : SafeHandle
			{
				public BufferedPaintHandle(IWin32Window w, SafeDCHandle hdcTarget, ref Rectangle rcTarget,
					ref BufferedPaintAnimationParams pAnimationParams, out IntPtr phdcFrom, out IntPtr phdcTo) : base(IntPtr.Zero, true)
				{
					RECT rc = rcTarget;
					SetHandle(BeginBufferedAnimation(new HandleRef(w, w.Handle), hdcTarget, ref rc, BufferedPaintBufferFormat.CompatibleBitmap, null, ref pAnimationParams, out phdcFrom, out phdcTo));
				}

				public override bool IsInvalid => handle == IntPtr.Zero;

				protected override bool ReleaseHandle()
				{
					EndBufferedAnimation(handle, true);
					return true;
				}
			}

			public static void Paint<TState, TParam>(Graphics g, IWin32Window w, Rectangle rc, PaintAction<TState, TParam> f, TState currentState, TState newState, int duration, TParam optParam)
			{
				try
				{
					BufferedPaintInit();
					if (Environment.OSVersion.Version.Major >= 6)
					{
						using (var hdc = new SafeDCHandle(g))
						{
							if (!hdc.IsInvalid)
							{
								// see if this paint was generated by a soft-fade animation
								if (!BufferedPaintRenderAnimation(new HandleRef(w, w.Handle), hdc))
								{
									var animParams = new BufferedPaintAnimationParams(duration);

									IntPtr hdcFrom, hdcTo;
									using (var h = new BufferedPaintHandle(w, hdc, ref rc, ref animParams, out hdcFrom, out hdcTo))
									{
										if (!h.IsInvalid)
										{
											if (hdcFrom != IntPtr.Zero)
											{
												using (var gfxFrom = Graphics.FromHdc(hdcFrom))
													f(gfxFrom, rc, currentState, optParam);
											}
											if (hdcTo != IntPtr.Zero)
											{
												using (var gfxTo = Graphics.FromHdc(hdcTo))
													f(gfxTo, rc, newState, optParam);
											}
										}
										else
										{
											hdc.Dispose();
											f(g, rc, newState, optParam);
										}
									}
								}
							}
						}
						BufferedPaintUnInit();
					}
					else
						f(g, rc, newState, optParam);
				}
				catch { }
			}
		}
		*/

		public static class BufferedPaint
		{
			static BufferedPaint() { BufferedPaintInit(); }

			public delegate void PaintAction<in TState, in TParam>(
				Graphics graphics, Rectangle bounds, TState currentState, TParam data);

			public static void Paint<TState, TParam>(Graphics graphics, Rectangle bounds, PaintAction<TState, TParam> paintAction,
				TState currentState, TParam data)
			{
				using (var g = new SafeDCHandle(graphics))
				using (var bp = new BufferedPaintHandle(g, bounds))
					paintAction(bp.Graphics, bounds, currentState, data);
			}

			public static void PaintAnimation<TState, TParam>(Graphics graphics, IWin32Window ctrl, Rectangle bounds,
				PaintAction<TState, TParam> paintAction, TState currentState, TState newState, int duration, TParam data)
			{
				try
				{
					if (System.Environment.OSVersion.Version.Major >= 6)
					{
						using (var hdc = new SafeDCHandle(graphics))
						{
							if (hdc.IsInvalid) goto defPaint;
							// see if this paint was generated by a soft-fade animation
							if (BufferedPaintRenderAnimation(new HandleRef(ctrl, ctrl.Handle), hdc)) return;

							using (var h = new BufferedPaintHandle(ctrl, hdc, bounds, new BufferedPaintAnimationParams(duration)))
							{
								if (!h.IsInvalid)
								{
									if (h.SourceGraphics != null)
										paintAction(h.SourceGraphics, bounds, currentState, data);
									if (h.Graphics != null)
										paintAction(h.Graphics, bounds, newState, data);
								}
								else
								{
									goto defPaint;
								}
							}
						}
						return;
					}
					defPaint:
					paintAction(graphics, bounds, newState, data);
				}
				catch { }
			}
		}

		public class BufferedPaintHandle : SafeHandle
		{
			private readonly bool ani;

			public BufferedPaintHandle(SafeDCHandle hdc, Rectangle targetRectangle, BufferedPaintParams paintParams = null,
				BufferedPaintBufferFormat fmt = BufferedPaintBufferFormat.CompatibleBitmap) : base(IntPtr.Zero, true)
			{
				RECT target = targetRectangle;
				IntPtr phdc;
				var hbp = BeginBufferedPaint(hdc, ref target, fmt, paintParams, out phdc);
				if (hbp == IntPtr.Zero) throw new Win32Exception();
				if (phdc != IntPtr.Zero) Graphics = Graphics.FromHdc(phdc);
				SetHandle(hbp);
			}

			public BufferedPaintHandle(IWin32Window wnd, SafeDCHandle hdc, Rectangle targetRectangle,
				BufferedPaintAnimationParams? animationParams = null,
				BufferedPaintParams paintParams = null, BufferedPaintBufferFormat fmt = BufferedPaintBufferFormat.CompatibleBitmap)
				: base(IntPtr.Zero, true)
			{
				RECT rc = targetRectangle;
				var ap = animationParams ?? BufferedPaintAnimationParams.Empty;
				IntPtr hdcFrom, hdcTo;
				var hbp = BeginBufferedAnimation(new HandleRef(wnd, wnd.Handle), hdc, ref rc, fmt, paintParams, ref ap, out hdcFrom,
					out hdcTo);
				if (hbp == IntPtr.Zero) throw new Win32Exception();
				if (hdcFrom != IntPtr.Zero) SourceGraphics = Graphics.FromHdc(hdcFrom);
				if (hdcTo != IntPtr.Zero) Graphics = Graphics.FromHdc(hdcTo);
				SetHandle(hbp);
				ani = true;
			}

			public Graphics Graphics { get; }

			public override bool IsInvalid => handle == IntPtr.Zero;

			public Graphics SourceGraphics { get; }

			protected override void Dispose(bool disposing)
			{
				if (disposing)
				{
					SourceGraphics?.Dispose();
					Graphics?.Dispose();
				}
				base.Dispose(disposing);
			}

			protected override bool ReleaseHandle()
			{
				try
				{
					if (ani)
						EndBufferedAnimation(handle, true);
					else
						EndBufferedPaint(handle, true);
					return true;
				}
				catch { }
				return false;
			}
		}
	}
}
